Completed
Push — 16.1 ( 6919a2...ed43f0 )
by Nathan
115:27 queued 101:15
created

AppJS.extend.getState   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 3
nop 0
dl 0
loc 22
rs 8.6737
c 0
b 0
f 0
1
/**
2
 * EGroupware - Addressbook - Javascript UI
3
 *
4
 * @link http://www.egroupware.org
5
 * @package addressbook
6
 * @author Hadi Nategh	<hn-AT-stylite.de>
7
 * @copyright (c) 2008-13 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
8
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
9
 * @version $Id$
10
 */
11
12
/**
13
 * UI for Addressbook
14
 *
15
 * @augments AppJS
16
 */
17
app.classes.addressbook = AppJS.extend(
18
{
19
	appname: 'addressbook',
20
21
	/**
22
	 * Constructor
23
	 *
24
	 * @memberOf app.addressbook
25
	 */
26
	init: function()
27
	{
28
		// call parent
29
		this._super.apply(this, arguments);
30
	},
31
32
	/**
33
	 * Destructor
34
	 */
35
	destroy: function()
36
	{
37
		// call parent
38
		this._super.apply(this, arguments);
39
	},
40
41
	/**
42
	 * This function is called when the etemplate2 object is loaded
43
	 * and ready.  If you must store a reference to the et2 object,
44
	 * make sure to clean it up in destroy().
45
	 *
46
	 * @param {etemplate2} et2 newly ready object
47
	 * @param {string} name
48
	 */
49
	et2_ready: function(et2, name)
50
	{
51
		// r49769 let's CRM view run under currentapp == "addressbook", which causes
52
		// app.addressbook.et2_ready called before app.infolog.et2_ready and therefore
53
		// app.addressbook.et2 would point to infolog template, if we not stop here
54
		if (name.match(/^infolog|tracker\./)) return;
55
56
		// call parent
57
		this._super.apply(this, arguments);
58
59
		switch (name)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
60
		{
61
			case 'addressbook.edit':
62
				var content = this.et2.getArrayMgr('content').data;
63
				if (typeof content.showsearchbuttons == 'undefined' || !content.showsearchbuttons)
64
				{
65
					this.show_custom_country(jQuery('select[id*="adr_one_countrycode"]').get(0));
66
					this.show_custom_country(jQuery('select[id*="adr_two_countrycode"]').get(0));
67
68
					// Instanciate infolog JS too - wrong app, so it won't be done automatically
69
					if(typeof window.app.infolog != 'object' && typeof window.app.classes['infolog'] == 'function')
70
					{
71
						window.app.infolog = new window.app.classes.infolog();
72
					}
73
				}
74
				// Call check value if the AB got opened with presets
75
				if (window.location.href.match(/&presets\[email\]/g) && content.presets_fields)
76
				{
77
					for(var i=0;i< content.presets_fields.length;i++)
78
					{
79
						this.check_value(this.et2.getWidgetById(content.presets_fields),0);
80
					}
81
				}
82
				break;
83
		}
84
85
		jQuery('select[id*="adr_one_countrycode"]').each(function() {
86
			app.addressbook.show_custom_country(this);
87
		});
88
		jQuery('select[id*="adr_two_countrycode"]').each(function() {
89
			app.addressbook.show_custom_country(this);
90
		});
91
	},
92
93
	/**
94
	 * Observer method receives update notifications from all applications
95
	 *
96
	 * App is responsible for only reacting to "messages" it is interested in!
97
	 *
98
	 * Addressbook checks for CRM view to update the displayed data if you edit
99
	 * that contact
100
	 *
101
	 * @param {string} _msg message (already translated) to show, eg. 'Entry deleted'
102
	 * @param {string} _app application name
103
	 * @param {(string|number)} _id id of entry to refresh or null
104
	 * @param {string} _type either 'update', 'edit', 'delete', 'add' or null
105
	 * - update: request just modified data from given rows.  Sorting is not considered,
106
	 *		so if the sort field is changed, the row will not be moved.
107
	 * - edit: rows changed, but sorting may be affected.  Requires full reload.
108
	 * - delete: just delete the given rows clientside (no server interaction neccessary)
109
	 * - add: requires full reload for proper sorting
110
	 * @param {string} _msg_type 'error', 'warning' or 'success' (default)
111
	 * @param {object|null} _links app => array of ids of linked entries
112
	 * or null, if not triggered on server-side, which adds that info
113
	 * @return {false|*} false to stop regular refresh, thought all observers are run
114
	 */
115
	observer: function(_msg, _app, _id, _type, _msg_type, _links)
116
	{
117
		// Edit to the current entry
118
		var state = this.getState();
119
		if(_app === 'addressbook' && state && state.type && state.type === 'view' && state.id === _id)
120
		{
121
			var content = egw.dataGetUIDdata('addressbook::'+_id);
122
			if (content.data)
123
			{
124
				var view = etemplate2.getById('addressbook-view');
125
				if(view)
126
				{
127
					view.widgetContainer._children[0].set_value({content:content.data});
128
				}
129
			}
130
			return false;
131
		}
132
		else if(_app === 'calendar')
133
		{
134
			// Event changed, update any [known] contacts participating
135
			var content = egw.dataGetUIDdata(_app+'::'+_id);
136
			if (content && content.data && content.data.participant_types && content.data.participant_types.c)
137
			{
138
				for(var contact in content.data.participant_types.c)
139
				{
140
					// Refresh handles checking to see if the contact is known,
141
					// and updating it directly
142
					egw.dataRefreshUID('addressbook::'+contact);
143
				}
144
				return true;
145
			}
146
			else if (!content)
147
			{
148
				// No data on the event, we'll have to reload if calendar column is visible
149
				// to get the updated information
150
				var nm = etemplate2.getById('addressbook-index').widgetContainer.getWidgetById('nm');
151
				var pref = nm ? nm._getPreferences() : false;
152
				if(pref && pref.visible.indexOf('calendar_calendar') > -1)
153
				{
154
					nm.refresh(null,'update');
155
				}
156
			}
157
		}
158
159
		return true;
160
	},
161
162
	/**
163
	 * Open CRM view
164
	 *
165
	 * @param _action
166
	 * @param _senders
167
	 */
168
	view: function(_action, _senders)
169
	{
170
		var index = _senders[0]._index;
171
		var id = _senders[0].id.split('::').pop();
172
		var extras = {
173
			index: index
174
		};
175
176
		// CRM list
177
		if(_action.id != 'view')
178
		{
179
			extras.crm_list = _action.id.replace('view-','');
180
		}
181
182
		this.egw.open(id, 'addressbook', 'view', extras, '_self', 'addressbook');
183
	},
184
185
	/**
186
	 * Set link filter for the already open & rendered  list
187
	 *
188
	 * @param {Object} filter Object with key / value pairs of filters to set
189
	 */
190
	view_set_list: function(filter)
191
	{
192
		// Find the infolog list
193
		var list = etemplate2.getById(
194
			jQuery(this.et2.getInstanceManager().DOMContainer).nextAll('.et2_container').attr('id')
195
		);
196
		var nm = list ? list.widgetContainer.getWidgetById('nm') : null;
197
		if(nm)
198
		{
199
			nm.applyFilters(filter);
200
		}
201
	},
202
203
	/**
204
	 * Run an action from CRM view toolbar
205
	 *
206
	 * @param {object} _action
207
	 */
208
	view_actions: function(_action)
209
	{
210
		var id = this.et2.getArrayMgr('content').data.id;
211
212
		switch(_action.id)
213
		{
214
			case 'open':
215
				this.egw.open(id, 'addressbook', 'edit');
216
				break;
217
			case 'copy':
218
				this.egw.open(id, 'addressbook', 'edit', { makecp: 1});
219
				break;
220
			case 'cancel':
221
				this.egw.open(null, 'addressbook', 'list', null, '_self', 'addressbook');
222
				break;
223
			default:	// submit all other buttons back to server
224
				this.et2._inst.submit();
225
				break;
226
		}
227
	},
228
229
	/**
230
	 * Open the calender to view the selected contacts
231
	 * @param {egwAction} _action
232
	 * @param {egwActionObject[]} _senders
233
	 */
234
	view_calendar: function(_action, _senders)
235
	{
236
		var extras = {
237
			filter: 'all',
238
			cat_id: '',
239
			owner: []
240
		};
241
		var orgs = [];
242
		for(var i = 0; i < _senders.length; i++)
243
		{
244
			// Remove UID prefix for just contact_id
245
			var ids = _senders[i].id.split('::');
246
			ids.shift();
247
			ids = ids.join('::');
248
249
			// Orgs need to get all the contact IDs first
250
			if (ids.substr(0,9) == 'org_name:')
251
			{
252
				orgs.push(ids);
253
			}
254
			else
255
			{
256
				// Check to see if this is a user account, we prefer to use
257
				// account ID in calendar
258
				var data = this.egw.dataGetUIDdata(_senders[i].id);
259
				if(data && data.data && data.data.account_id)
260
				{
261
					extras.owner.push(data.data.account_id);
262
				}
263
				else
264
				{
265
					extras.owner.push('c'+ids);
266
				}
267
			}
268
		}
269
270
		if(orgs.length > 0)
271
		{
272
			// Get organisation contacts, then show infolog list
273
			this.egw.json('addressbook.addressbook_ui.ajax_organisation_contacts',
274
				[orgs],
275
				function(contacts) {
276
					for(var i = 0; i < contacts.length; i++)
277
					{
278
						extras.owner.push('c'+contacts[i]);
279
					}
280
					extras.owner = extras.owner.join(',');
281
					this.egw.open('','calendar','list',extras,'calendar');
282
				},this,true,this
283
			).sendRequest();
284
		}
285
		else
286
		{
287
			extras.owner = extras.owner.join(',');
288
			egw.open('', 'calendar', 'list', extras, 'calendar');
289
		}
290
	},
291
	/**
292
	 * Add appointment or show calendar for selected contacts, call default nm_action after some checks
293
	 *
294
	 * @param _action
295
	 * @param _senders
296
	 */
297
	add_cal: function(_action, _senders)
298
	{
299
		if (!_senders[0].id.match(/^(?:addressbook::)?[0-9]+$/))
300
		{
301
			// send org-view requests to server
302
			_action.data.nm_action = "submit";
303
			nm_action(_action, _senders);
304
		}
305
		else
306
		{
307
			var ids = egw.user('account_id')+',';
308
			for (var i = 0; i < _senders.length; i++)
309
			{
310
				// Remove UID prefix for just contact_id
311
				var id = _senders[i].id.split('::');
312
				ids += "c" + id[1] + ((i < _senders.length - 1) ? "," : "");
313
			}
314
			var extra = {};
315
			extra[_action.data && _action.data.url && _action.data.url.indexOf('owner') > 0 ? 'owner' : 'participants'] = ids;
316
317
			// Use framework to add calendar entry
318
			egw.open('','calendar','add',extra);
319
		}
320
	},
321
322
	/**
323
	 * View infolog entries linked to selected contact
324
	 * @param {egwAction} _action Select action
325
	 * @param {egwActionObject[]} _senders Selected contact(s)
326
	 */
327
	view_infolog: function(_action, _senders)
328
	{
329
		var extras = {
330
			action: 'addressbook',
331
			action_id: [],
332
			action_title: _senders.length > 1 ? this.egw.lang('selected contacts') : ''
333
		};
334
		var orgs = [];
335
		for(var i = 0; i < _senders.length; i++)
336
		{
337
			// Remove UID prefix for just contact_id
338
			var ids = _senders[i].id.split('::');
339
			ids.shift();
340
			ids = ids.join('::');
341
342
			// Orgs need to get all the contact IDs first
343
			if (ids.substr(0,9) == 'org_name:')
344
			{
345
				orgs.push(ids);
346
			}
347
			else
348
			{
349
				extras.action_id.push(ids);
350
			}
351
		}
352
353
		if(orgs.length > 0)
354
		{
355
			// Get organisation contacts, then show infolog list
356
			this.egw.json('addressbook.addressbook_ui.ajax_organisation_contacts',
357
				[orgs],
358
				function(contacts) {
359
					extras.action_id = extras.action_id.concat(contacts);
360
					this.egw.open('','infolog','list',extras,'infolog');
361
				},this,true,this
362
			).sendRequest();
363
		}
364
		else
365
		{
366
			egw.open('', 'infolog', 'list', extras, 'infolog');
367
		}
368
	},
369
370
	/**
371
	 * Add task for selected contacts, call default nm_action after some checks
372
	 *
373
	 * @param _action
374
	 * @param _senders
375
	 */
376
	add_task: function(_action, _senders)
377
	{
378
		if (!_senders[0].id.match(/^(addressbook::)?[0-9]+$/))
379
		{
380
			// send org-view requests to server
381
			_action.data.nm_action = "submit";
382
		}
383
		else
384
		{
385
			// call nm_action's popup
386
			_action.data.nm_action = "popup";
387
		}
388
		nm_action(_action, _senders);
389
	},
390
391
	/**
392
	 * [More...] in phones clicked: copy allways shown phone numbers to phone popup
393
	 *
394
	 * @param {jQuery.event} _event
395
	 * @param {et2_widget} _widget
396
	 */
397
	showphones: function(_event, _widget)
398
	{
399
		this._copyvalues({
400
			tel_home: 'tel_home2',
401
			tel_work: 'tel_work2',
402
			tel_cell: 'tel_cell2',
403
			tel_fax:  'tel_fax2'
404
		});
405
		jQuery('table.editphones').css('display','inline');
406
407
		_event.stopPropagation();
408
		return false;
409
	},
410
411
	/**
412
	 * [OK] in phone popup clicked: copy phone numbers back to always shown ones
413
	 *
414
	 * @param {jQuery.event} _event
415
	 * @param {et2_widget} _widget
416
	 */
417
	hidephones: function(_event, _widget)
418
	{
419
		this._copyvalues({
420
			tel_home2: 'tel_home',
421
			tel_work2: 'tel_work',
422
			tel_cell2: 'tel_cell',
423
			tel_fax2:  'tel_fax'
424
		});
425
		jQuery('table.editphones').css('display','none');
426
427
		_event.stopPropagation();
428
		return false;
429
	},
430
431
	/**
432
	 * Copy content of multiple fields
433
	 *
434
	 * @param {object} what object with src: dst pairs
435
	 */
436
	_copyvalues: function(what)
437
	{
438
		for(var name in what)
439
		{
440
			var src = this.et2.getWidgetById(name);
441
			var dst = this.et2.getWidgetById(what[name]);
442
			if (src && dst) dst.set_value(src.get_value ? src.get_value() : src.value);
443
		}
444
		// change tel_prefer according to what
445
		var tel_prefer = this.et2.getWidgetById('tel_prefer');
446
		if (tel_prefer)
447
		{
448
			var val = tel_prefer.get_value ? tel_prefer.get_value() : tel_prefer.value;
449
			if (typeof what[val] != 'undefined') tel_prefer.set_value(what[val]);
450
		}
451
	},
452
453
	/**
454
	 * Callback function to create confirm dialog for duplicates contacts
455
	 *
456
	 * @param {object} _data includes duplicates contacts information
457
	 *
458
	 */
459
	_confirmdialog_callback: function(_data)
460
	{
461
		var confirmdialog = function(_title, _value, _buttons, _egw_or_appname)
462
		{
463
			return et2_createWidget("dialog",
464
			{
465
				callback: function(_buttons, _value)
466
				{
467
					if (_buttons == et2_dialog.OK_BUTTON)
468
					{
469
						var id = '';
470
						var content = this.template.widgetContainer.getArrayMgr('content').data;
471
						for (var row in _value.grid)
472
						{
473
							if (_value.grid[row].confirm == "true" && typeof content.grid !='undefined')
474
							{
475
								id = this.options.value.content.grid[row].confirm;
476
								egw.open(id, 'addressbook');
477
478
							}
479
						}
480
					}
481
				},
482
				title: _title||egw.lang('Input required'),
483
				buttons: _buttons||et2_dialog.BUTTONS_OK_CANCEL,
484
				value: {
485
					content: {
486
						grid: _value
487
					}
488
				},
489
				template: egw.webserverUrl+'/addressbook/templates/default/dupconfirmdialog.xet'
490
			}, et2_dialog._create_parent(_egw_or_appname));
491
		};
492
493
		if (_data.msg && _data.doublicates)
494
		{
495
			var content = [];
496
497
			for(var id in _data.doublicates)
498
			{
499
				content.push({"confirm":id,"name":_data.doublicates[id]});
500
			}
501
			confirmdialog(this.egw.lang('Duplicate warning'),content,et2_dialog.BUTTONs_OK_CANCEL);
502
		}
503
		if (typeof _data.fileas_options == 'object' && this.et2)
504
		{
505
			var selbox = this.et2.getWidgetById('fileas_type');
506
			if (selbox)
507
			{
508
				selbox.set_select_options(_data.fileas_sel_options);
509
			}
510
		}
511
	},
512
513
	/**
514
	 * Callback if certain fields get changed
515
	 *
516
	 * @param {widget} widget widget
517
	 * @param {string} own_id Current AB id
518
	 */
519
	check_value: function(widget, own_id)
520
	{
521
		// if we edit an account, call account_change to let it do it's stuff too
522
		if (this.et2.getWidgetById('account_lid'))
523
		{
524
			this.account_change(null, widget);
525
		}
526
527
		var values = this.et2._inst.getValues(this.et2);
528
529
		if (widget.id.match(/n_/))
530
		{
531
			var value = '';
532
			if (values.n_prefix) value += values.n_prefix+" ";
533
			if (values.n_given)  value += values.n_given+" ";
534
			if (values.n_middle) value += values.n_middle+" ";
535
			if (values.n_family) value += values.n_family+" ";
536
			if (values.n_suffix) value += values.n_suffix;
537
538
			var name = this.et2.getWidgetById("n_fn");
539
			if (typeof name != 'undefined')	name.set_value(value);
540
		}
541
		egw.json('addressbook.addressbook_ui.ajax_check_values', [values, widget.id, own_id],this._confirmdialog_callback,this,true,this).sendRequest();
542
	},
543
544
	show_custom_country: function(selectbox)
545
	{
546
		if(!selectbox) return;
547
		var custom_field_name = selectbox.id.replace("countrycode", "countryname");
548
		var custom_field = document.getElementById(custom_field_name);
549
		if(custom_field && selectbox.value == "-custom-") {
550
			custom_field.style.display = "inline";
551
		}
552
		else if (custom_field)
553
		{
554
			if((selectbox.value == "" || selectbox.value == null) && custom_field.value != "")
555
			{
556
				selectbox.value = "-custom-";
557
				// Chosen needs this to update
558
				jQuery(selectbox).trigger("liszt:updated");
559
560
				custom_field.style.display = "inline";
561
			}
562
			else
563
			{
564
				custom_field.style.display = "none";
565
			}
566
		}
567
	},
568
569
	/**
570
	 * Add a new mailing list.  If any contacts are selected, they will be added.
571
	 *
572
	 * @param {egwAction} owner
573
	 * @param {egwActionObject[]} selected
574
	 */
575
	add_new_list: function(owner, selected)
576
	{
577
		if(!owner || typeof owner == 'object')
578
		{
579
			var filter = this.et2.getWidgetById('filter');
580
			owner = filter.getValue()||egw.preference('add_default','addressbook');
581
		}
582
		var contacts = [];
583
		if(selected && selected[0] && selected[0].getAllSelected())
584
		{
585
			// Action says all contacts selected, better ask the server for _all_ the IDs
586
			var fetching = fetchAll(selected, this.et2.getWidgetById('nm'), jQuery.proxy(
587
				function(contacts) {
588
					this._add_new_list_prompt(owner, contacts);
589
				}, this));
590
			if (fetching) return;
591
		}
592
		if(selected && selected.length)
593
		{
594
			for(var i = 0; i < selected.length; i++)
595
			{
596
				// Remove UID prefix for just contact_id
597
				var ids = selected[i].id.split('::');
598
				ids.shift();
599
				ids = ids.join('::');
600
				contacts.push(ids);
601
			}
602
		}
603
		this._add_new_list_prompt(owner, contacts);
604
	},
605
606
	/**
607
	 * Ask the user for a name, then create a new list with the provided contacts
608
	 * in it.
609
	 *
610
	 * @param {int} owner
611
	 * @param {String[]} contacts
612
	 */
613
	_add_new_list_prompt: function(owner, contacts)
614
	{
615
		var lists = this.et2.getWidgetById('filter2');
616
		et2_dialog.show_prompt(
617
			function(button, name) {
618
				if(button == et2_dialog.OK_BUTTON)
619
				{
620
					egw.json('addressbook.addressbook_ui.ajax_set_list',[0, name, owner, contacts],
621
						function(result)
622
						{
623
							if(typeof result == 'object') return; // This response not for us
624
							// Update list
625
							if(result)
626
							{
627
								lists.options.select_options.unshift({value:result,label:name});
628
								lists.set_select_options(lists.options.select_options);
629
630
								// Set to new list so they can see it easily
631
								lists.set_value(result);
632
								// Call cahnge event manually after setting the value
633
								// Not sure why our selectbox does not trigger change event
634
								jQuery(lists.node).change();
635
							}
636
						}
637
					).sendRequest(true);
638
				}
639
			},
640
			this.egw.lang('Name for the distribution list'),
641
			this.egw.lang('Add a new list')
642
		);
643
	},
644
645
	/**
646
	 * Rename the current distribution list selected in the nextmatch filter2
647
	 *
648
	 * Differences from add_new_list are in the dialog, parameters sent, and how the
649
	 * response is dealt with
650
	 *
651
	 * @param {egwAction} action Action selected in context menu (rename)
652
	 * @param {egwActionObject[]} selected The selected row(s).  Not used for this.
653
	 */
654
	rename_list: function(action, selected)
655
	{
656
		var lists = this.et2.getWidgetById('filter2');
657
		var list = lists.getValue() || 0;
658
		var value = null;
659
		for(var i = 0; i < lists.options.select_options.length; i++)
660
		{
661
			if(lists.options.select_options[i].value == list)
662
			{
663
				value = lists.options.select_options[i];
664
			}
665
		}
666
		et2_dialog.show_prompt(
667
			function(button, name) {
668
				if(button == et2_dialog.OK_BUTTON)
669
				{
670
					egw.json('addressbook.addressbook_ui.ajax_set_list',[list, name],
671
						function(result)
672
						{
673
							if(typeof result == 'object') return; // This response not for us
674
							// Update list
675
							if(result)
676
							{
677
								value.label = name;
678
								lists.set_select_options(lists.options.select_options);
679
							}
680
						}
681
					).sendRequest(true);
682
				}
683
			},
684
			this.egw.lang('Name for the distribution list'),
685
			this.egw.lang('Rename list'),
686
			value.label
687
		);
688
	},
689
690
	/**
691
	 * OnChange for distribution list selectbox
692
	 */
693
	filter2_onchange: function()
694
	{
695
		var filter = this.et2.getWidgetById('filter');
696
		var filter2 = this.et2.getWidgetById('filter2');
697
		var widget = this.et2.getWidgetById('nm');
698
		var filter2_val = filter2.get_value();
699
700
		if(filter2_val == 'add')
701
		{
702
			this.add_new_list(typeof widget == 'undefined' ? this.et2.getWidgetById('filter').value : widget.header.filter.get_value());
703
			this.value='';
704
		}
705
		// automatic switch to accounts addressbook or all addressbooks depending on distribution list is a group
706
		else if (filter2_val && (filter2_val < 0) !== (filter.get_value() === '0'))
707
		{
708
			// Change filter & filter2 at the same time
709
			widget.applyFilters({
710
				filter: filter2_val < 0 ? '0' : '',
711
				filter2: filter2_val
712
			});
713
			// Don't get rows here, let applyFilters() do it
714
			return false;
715
		}
716
		
717
		return true;
718
	},
719
720
	/**
721
	 * Method to enable actions by comparing a field with given value
722
	 */
723
	nm_compare_field: function()
724
	{
725
		var field = this.et2.getWidgetById('filter2');
726
		if (field) var val = field.get_value();
727
		if (val)
728
		{
729
			return nm_compare_field;
730
		}
731
		else
732
		{
733
			return false;
734
		}
735
	},
736
737
	/**
738
	 * Apply advanced search filters to index nextmatch
739
	 *
740
	 * @param {object} filters
741
	 */
742
	adv_search: function(filters)
743
	{
744
		var index = window.opener.etemplate2.getById('addressbook-index');
745
		if(!index)
746
		{
747
			alert('Could not find index');
748
			egw(window).close();
749
			return false;
750
		}
751
		var nm = index.widgetContainer.getWidgetById('nm');
752
		if(!index)
753
		{
754
			window.opener.egw.message('Could not find list', 'error');
755
			egw(window).close();
756
			return false;
757
		}
758
		// Reset filters first
759
		nm.activeFilters = {};
760
		nm.applyFilters(filters);
761
		return false;
762
	},
763
764
	/**
765
	 * Mail vCard
766
	 *
767
	 * @param {object} _action
768
	 * @param {array} _elems
769
	 */
770
	adb_mail_vcard: function(_action, _elems)
771
	{
772
		var app_registry = egw.link_get_registry('mail');
773
		var link = egw().link("/index.php","menuaction="+app_registry['add']['menuaction']);
774
		for (var i = 0; i < _elems.length; i++)
775
		{
776
			var idToUse = _elems[i].id;
777
			var idToUseArray = idToUse.split('::');
778
			idToUse = idToUseArray[1];
779
			link += "&preset[type][]="+encodeURIComponent("text/vcard; charset="+(egw.preference('vcard_charset', 'addressbook') || 'utf-8'));
780
			link += "&preset[file][]="+encodeURIComponent("vfs://default/apps/addressbook/"+idToUse+"/.entry");
781
		}
782
		if (typeof app_registry['view'] != 'undefined' && typeof app_registry['view_popup'] != 'undefined' )
783
		{
784
			var w_h =app_registry['view_popup'].split('x');
785
			if (w_h[1] == 'egw_getWindowOuterHeight()') w_h[1] = (screen.availHeight>egw_getWindowOuterHeight()?screen.availHeight:egw_getWindowOuterHeight());
786
			egw_openWindowCentered2(link, '_blank', w_h[0], w_h[1], 'yes');
787
		}
788
789
	},
790
791
	/**
792
	 * Action function to set business or private mail checkboxes to user preferences
793
	 *
794
	 * @param {egwAction} action Action user selected.
795
	 */
796
	mailCheckbox: function(action)
797
	{
798
		var preferences = {
799
			business: action.getManager().getActionById('email_business').checked ? true : false,
800
			private: action.getManager().getActionById('email_home').checked ? true : false
801
		};
802
		this.egw.set_preference('addressbook','preferredMail', preferences);
803
	},
804
805
	/**
806
	 * Action function to add the email address (business or home) of the selected
807
	 * contacts to a compose email popup window.
808
	 *
809
	 * Uses the egw API to handle the opening of the popup.
810
	 *
811
	 * @param {egwAction} action Action user selected.  Should have ID of either
812
	 *  'email_business' or 'email_home', from server side definition of actions.
813
	 * @param {egwActionObject[]} selected Selected rows
814
	 */
815
	addEmail: function(action, selected)
816
	{
817
		// Check for all selected.
818
		var nm = this.et2.getWidgetById('nm');
819
		if(fetchAll(selected, nm, jQuery.proxy(function(ids) {
820
			// fetchAll() returns just the ID, no prefix, so map it to match normal selected
821
			this.addEmail(action, ids.map(function(num) {return {id:'addressbook::'+num};}));
822
		}, this)))
823
		{
824
			// Need more IDs, will use the above callback when they're ready.
825
			return;
826
		}
827
828
		// Go through selected & pull email addresses from data
829
		var emails = [];
830
		for(var i = 0; i < selected.length; i++)
831
		{
832
			// Pull data from global cache
833
			var data = egw.dataGetUIDdata(selected[i].id) || {data:{}};
834
835
			var email_business = data.data[action.getManager().getActionById('email_business').checked ? 'email' : ''];
836
			var email = data.data[action.getManager().getActionById('email_home').checked ? 'email_home' : ''];
837
			// prefix email with full name
838
			var personal = data.data.n_fn || '';
839
			if (personal.match(/[^a-z0-9. -]/i)) personal = '"'+personal.replace(/"/, '\\"')+'"';
840
841
			if(email_business)
842
			{
843
				emails.push((personal?personal+' <':'')+email_business+(personal?'>':''));
844
			}
845
			if(email)
846
			{
847
				emails.push((personal?personal+' <':'')+email+(personal?'>':''));
848
			}
849
		}
850
		switch (action.id)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
851
		{
852
			case "add_to_to":
853
				egw.open_link('mailto:' + emails.join(','));
854
				break;
855
			case "add_to_cc":
856
				egw.open_link('mailto:' + '?cc='  + emails.join(','));
857
				//egw.mailto('mailto:');
858
				break;
859
			case "add_to_bcc":
860
				egw.open_link('mailto:' + '?bcc=' + emails.join(','));
861
				break;
862
		}
863
864
		return false;
865
	},
866
867
	/**
868
	 * Retrieve the current state of the application for future restoration
869
	 *
870
	 * Overridden from parent to handle viewing a contact.  In this case state
871
	 * will be {contact_id: #}
872
	 *
873
	 * @return {object} Application specific map representing the current state
874
	 */
875
	getState: function()
876
	{
877
		// Most likely we're in the list view
878
		var state = this._super.apply(this, arguments);
879
880
		if(jQuery.isEmptyObject(state))
881
		{
882
			// Not in a list view.  Try to find contact ID
883
			var etemplates = etemplate2.getByApplication('addressbook');
884
			for(var i = 0; i < etemplates.length; i++)
885
			{
886
				var content = etemplates[i].widgetContainer.getArrayMgr("content");
887
				if(content && content.getEntry('id'))
888
				{
889
					state = {app: 'addressbook', id: content.getEntry('id'), type: 'view'};
890
					break;
891
				}
892
			}
893
		}
894
895
		return state;
896
	},
897
898
	/**
899
	 * Set the application's state to the given state.
900
	 *
901
	 * Overridden from parent to stop the contact view's infolog nextmatch from
902
	 * being changed.
903
	 *
904
	 * @param {{name: string, state: object}|string} state Object (or JSON string) for a state.
905
	 *	Only state is required, and its contents are application specific.
906
	 *
907
	 * @return {boolean} false - Returns false to stop event propagation
908
	 */
909
	setState: function(state)
910
	{
911
		var current_state = this.getState();
912
913
		// State should be an object, not a string, but we'll parse
914
		if(typeof state == "string")
915
		{
916
			if(state.indexOf('{') != -1 || state =='null')
917
			{
918
				state = JSON.parse(state);
919
			}
920
		}
921
922
923
		// Redirect from view to list - parent would do this, but infolog nextmatch stops it
924
		if(current_state.app && current_state.id && (typeof state.state == 'undefined' || typeof state.state.app == 'undefined'))
925
		{
926
			// Redirect to list
927
			// 'blank' is the special name for no filters, send that instead of the nice translated name
928
			var safe_name = jQuery.isEmptyObject(state) || jQuery.isEmptyObject(state.state||state.filter) ? 'blank' : state.name.replace(/[^A-Za-z0-9-_]/g, '_');
929
			egw.open('',this.appname,'list',{'favorite': safe_name},this.appname);
930
			return false;
931
		}
932
		else if (jQuery.isEmptyObject(state))
933
		{
934
			// Regular handling first to clear everything but advanced search
935
			this._super.apply(this, arguments);
936
937
			// Clear advanced search, which is in session and etemplate
938
			egw.json('addressbook.addressbook_ui.ajax_clear_advanced_search',[], function() {
939
				framework.setWebsiteTitle('addressbook','');
940
				var index = etemplate2.getById('addressbook-index');
941
				if(index && index.widgetContainer)
942
				{
943
					var nm = index.widgetContainer.getWidgetById('nm');
944
					if(nm)
945
					{
946
						nm.applyFilters({
947
							advanced_search: false
948
						});
949
					}
950
				}
951
			},this).sendRequest(true);
952
			return false;
953
		}
954
		else if (state.state.grouped_view)
955
		{
956
			// Deal with grouped views that are not valid (not in list of options)
957
			// by faking viewing that organisation
958
			var index = etemplate2.getById('addressbook-index');
959
			if(index && index.widgetContainer)
960
			{
961
				var grouped = index.widgetContainer.getWidgetById('grouped_view');
962
				var options = false;
963
				if(grouped && grouped.options && grouped.options.select_options)
964
				{
965
					options = grouped.options.select_options;
966
				}
967
968
				// Check to see if it's not there
969
				if(options && (options.find && 
970
					!grouped.options.select_options.find(function(e) {console.log(e); return e.value === state.state.grouped_view;}) ||
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
971
					!options[state.state.grouped_view]
972
				))
973
				{
974
					var nm = index.widgetContainer.getWidgetById('nm');
975
					var action = nm.controller._actionManager.getActionById('view_org');
976
					var senders = [{_context: {_widget: nm}}];
977
					return nm_action(action, senders, {}, {ids:[state.state.grouped_view]});
978
				}
979
			}
980
		}
981
982
		// Make sure advanced search is false if not set, this clears any
983
		// currently set advanced search
984
		if(typeof state.state.advanced_search === 'undefined')
985
		{
986
			state.state.advanced_search = false;
987
		}
988
		return this._super.apply(this, arguments);
989
	},
990
991
	/**
992
	 * Field changed, call server validation
993
	 *
994
	 * @param {jQuery.Event} _ev
995
	 * @param {et2_button} _widget
996
	 */
997
	account_change: function(_ev, _widget)
998
	{
999
		switch(_widget.id)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
1000
		{
1001
			case 'account_lid':
1002
			case 'n_family':
1003
			case 'n_given':
1004
			case 'account_passwd':
1005
			case 'account_passwd_2':
1006
				var values = this.et2._inst.getValues(this.et2);
1007
				var data = {
1008
					account_id: this.et2.getArrayMgr('content').data.account_id,
1009
					account_lid: values.account_lid,
1010
					account_firstname: values.n_given,
1011
					account_lastname: values.n_family,
1012
					account_email: values.email,
1013
					account_passwd: values.account_passwd,
1014
					account_passwd_2: values.account_passwd_2
1015
				};
1016
1017
				this.egw.message('');
1018
				this.egw.json('admin_account::ajax_check', [data, _widget.id], function(_msg)
1019
				{
1020
					if (_msg && typeof _msg == 'string')
1021
					{
1022
						egw(window).message(_msg, 'error');	// context get's lost :(
1023
						_widget.getDOMNode().focus();
1024
					}
1025
				}, this).sendRequest();
1026
				break;
1027
		}
1028
	},
1029
1030
	/**
1031
	 * Get title in order to set it as document title
1032
	 * @returns {string}
1033
	 */
1034
	getWindowTitle: function()
1035
	{
1036
		var widget = this.et2.getWidgetById('n_fn');
1037
		if(widget) return widget.options.value;
1038
	},
1039
1040
	/**
1041
	 * Enable/Disable geolocation action items in contextmenu base on address availabilty
1042
	 *
1043
	 * @param {egwAction} _action
1044
	 * @param {egwActionObject[]} _selected selected rows
1045
	 * @returns {boolean} return false if no address found
1046
	 */
1047
	geoLocation_enabled: function(_action, _selected)
1048
	{
1049
		// multiple selection is not supported
1050
		if (_selected.length>1) return false;
1051
1052
		var url = this.getGeolocationConfig();
1053
1054
		// exit if no url or invalide url given
1055
		if (!url || typeof url === 'undefined' || typeof url !== 'string')
1056
		{
1057
			egw.debug('warn','no url or invalid url given as geoLocationUrl');
1058
			return false;
1059
		}
1060
		var content = egw.dataGetUIDdata(_selected[0].id);
1061
1062
		// Selected, but data not found
1063
		if(!content || typeof content.data === 'undefined') return false;
1064
1065
		var type = _action.id === 'business'?'one':'two';
1066
		var addrs = [
1067
			content.data['adr_'+type+'_street'],
1068
			content.data['adr_'+type+'_locality'],
1069
			content.data['adr_'+type+'_postalcode']
1070
		];
1071
1072
		var fields = '';
1073
		// Replcae placeholders with acctual values
1074
		for (var i=0;i < addrs.length; i++)
1075
		{
1076
			fields += addrs[i] ? addrs[i] : '';
1077
		}
1078
		return (url !== '' && fields !== '')?true:false;
1079
	},
1080
1081
	/**
1082
	 * Generate a geo location URL based on geolocation_url in
1083
	 * site configuration
1084
	 *
1085
	 * @param {object} _dest_data
1086
	 * @param {string} _dest_type type of destination address ('one'| 'two')
1087
	 * @param {object} _src_data address data to be used as source contact data|coordination object
1088
	 * @param {string} _src_type type of source address ('browser'|'one'|'two')
1089
	 * @returns {Boolean|string} return url and return false if no address
1090
	 */
1091
	geoLocationUrl: function (_dest_data, _dest_type,_src_data, _src_type)
1092
	{
1093
		var dest_type = _dest_type || 'one';
1094
		var url = this.getGeolocationConfig();
1095
1096
		// exit if no url or invalide url given
1097
		if (!url || typeof url === 'undefined' || typeof url !== 'string')
1098
		{
1099
			egw.debug('warn','no url or invalid url given as geoLocationUrl');
1100
			return false;
1101
		}
1102
1103
		// array of placeholders with their representing values
1104
		var	addrs = [
1105
1106
			[ // source address
1107
				{id:'r0',val:_src_type === 'browser'?_src_data.latitude:_src_data['adr_'+_src_type+'_street']},
1108
				{id:'t0',val:_src_type === 'browser'?_src_data.longitude:_src_data['adr_'+_src_type+'_locality']},
1109
				{id:'c0',val:_src_type === 'browser'?'':_src_data['adr_'+_src_type+'_countrycode']},
1110
				{id:'z0',val:_src_type === 'browser'?'':_src_data['adr_'+_src_type+'_postalcode']}
1111
			],
1112
			[ // destination address
1113
				{id:'r1',val:_dest_data['adr_'+dest_type+'_street']},
1114
				{id:'t1',val:_dest_data['adr_'+dest_type+'_locality']},
1115
				{id:'c1',val:_dest_data['adr_'+dest_type+'_countrycode']},
1116
				{id:'z1',val:_dest_data['adr_'+dest_type+'_postalcode']}
1117
			]
1118
		];
1119
1120
		var src_param = url.match(/{{%rs=.*%rs}}/ig);
1121
		if (src_param[0])
1122
		{
1123
			src_param = src_param[0].replace(/{{%rs=/,'');
1124
			src_param = src_param.replace(/%rs}}/,'');
1125
			url = url.replace(/{{%rs=.*%rs}}/, src_param);
1126
		}
1127
1128
		var d_param = url.match(/{{%d=.*%d}}/ig);
1129
		if (d_param[0])
1130
		{
1131
			d_param = d_param[0].replace(/{{%d=/,'');
1132
			d_param = d_param.replace(/%d}}/,'');
1133
			url = url.replace(/{{%d=.*%d}}/, d_param);
1134
		}
1135
1136
		// Replcae placeholders with acctual values
1137
		for (var j=0;j<addrs.length;j++)
1138
		{
1139
			for (var i=0;i < addrs[j].length; i++)
1140
			{
1141
				url = url.replace('%'+addrs[j][i]['id'], addrs[j][i]['val']? addrs[j][i]['val'] : "");
1142
			}
1143
		}
1144
		return url !== ''? url : false;
1145
	},
1146
1147
	/**
1148
	 * Open a popup base on selected address in provided map
1149
	 *
1150
	 * @param {object} _action
1151
	 * @param {object} _selected
1152
	 */
1153
	geoLocationExec: function (_action, _selected)
1154
	{
1155
		var content = egw.dataGetUIDdata(_selected[0].id);
1156
		var geolocation_src = egw.preference('geolocation_src','addressbook');
1157
		var self = this;
1158
1159
		if (geolocation_src === 'browser' && navigator.geolocation)
1160
		{
1161
			navigator.geolocation.getCurrentPosition(function(position){
1162
				if (position && position.coords)
1163
				{
1164
					var url = self.geoLocationUrl(content.data,_action.id === 'business'?'one':'two', position.coords, 'browser');
1165
					window.open(url,'_blank');
1166
				}
1167
			});
1168
		}
1169
		else
1170
		{
1171
			egw.json('addressbook.addressbook_ui.ajax_get_contact', [egw.user('account_id')],function(_data){
1172
				var url = self.geoLocationUrl(content.data,_action.id === 'business'?'one':'two', _data, geolocation_src === 'browser'?'one':geolocation_src);
1173
				window.open(url,'_blank');
1174
			}).sendRequest();
1175
1176
		}
1177
	},
1178
1179
	/**
1180
	 * Get geolocation_url stored in config|default url
1181
	 *
1182
	 * @returns {String}
1183
	 */
1184
	getGeolocationConfig: function()
1185
	{
1186
		// This default url should be identical to the first value of geolocation_url array
1187
		// defined in addressbook_hooks::config
1188
		var default_url = 'https://maps.here.com/directions/drive{{%rs=/%rs}}%r0,%t0,%z0,%c0{{%d=/%d}}%r1,%t1,%z1+%c1';
1189
		var geo_url = egw.config('geolocation_url');
1190
		if (geo_url) geo_url = geo_url[0];
1191
		return geo_url || default_url;
1192
	}
1193
});
1194